/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file rw_t5t_common.c
 * @brief T5T tag command implementation
 */

#include "nci_feature.h"

#if IS_FEATURE_ON(NCI_FEATURE_RW_T5T)

#include "rw_t5t_common.h"
#include "nfc_log_api.h"
#include "nfc_osi_api.h"
#include "nci_utils.h"
#include "rw_int.h"
#include "discover_int.h"

static bool rw_i93_process_sys_info(uint8_t* p_data, uint16_t length);
static bool rw_i93_process_ext_sys_info(uint8_t* p_data, uint16_t length);
static void assemble_cmd_read_single_block(tNCI_BUF *p_cmd, uint32_t block_number, bool read_security);
static void assemble_cmd_inventory(tNCI_BUF *p_cmd, uint8_t* p_uid, bool including_afi, uint8_t afi);
static bool get_ic_reference_in_sys_info(uint16_t length, uint8_t* p, uint8_t* p_uid);
static bool get_block_status_ons();
static bool get_ic_reference_in_ext_sys_info(uint16_t length, uint8_t* p, uint8_t* p_uid);
static bool rw_i93_check_sys_info_prot_ext(uint8_t error_code);
static tNCI_STATUS get_ext_sys_info();
static tNCI_STATUS get_sys_info_with_prot_ext_flag();
static tNCI_STATUS process_sys_info(uint8_t* p_rsp_data, uint16_t length);

#if IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)
static char* rw_i93_get_tag_name(uint8_t product_version);
static void assemble_cmd_write_single_block(tNCI_BUF *p_cmd, uint32_t block_number,  uint8_t* p_data);
static void assemble_cmd_read_multi_blocks(tNCI_BUF *p_cmd, uint32_t first_block_number, uint32_t number_blocks);
static void assemble_cmd_lock_block(tNCI_BUF *p_cmd, uint32_t block_number);
static void assemble_cmd_get_multi_block_sec(tNCI_BUF *p_cmd, uint32_t first_block_number, uint32_t number_blocks);
static void get_product_version_st();
static void get_product_version_ons();
static void get_product_version_ti(uint8_t* p_uid);
static uint32_t recalculate_num_block_ones(uint32_t first_block, uint32_t num_block);
static tNCI_STATUS multi_read_next_blocks(uint32_t first_block, tNCI_BUF* p_rsp);
#endif // IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)

static bool get_block_status_ons()
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    /*
    **  N36RW02:  00011010(b), blockSize: 4, numberBlocks: 0x40
    **  N24RF04:  00101110(b), blockSize: 4, numberBlocks: 0x80
    **  N24RF04E: 00101010(b), blockSize: 4, numberBlocks: 0x80
    **  N24RF16:  01001010(b), blockSize: 4, numberBlocks: 0x200
    **  N24RF16E: 01001110(b), blockSize: 4, numberBlocks: 0x200
    **  N24RF64:  01101010(b), blockSize: 4, numberBlocks: 0x800
    **  N24RF64E: 01101110(b), blockSize: 4, numberBlocks: 0x800
    */
    p_i93->block_size = 4;
    switch (p_i93->product_version) {
        case RW_I93_ONS_N36RW02:
            p_i93->num_block = 0x40;
            break;
        case RW_I93_ONS_N24RF04:
        case RW_I93_ONS_N24RF04E:
            p_i93->num_block = 0x80;
            break;
        case RW_I93_ONS_N24RF16:
        case RW_I93_ONS_N24RF16E:
            p_i93->num_block = 0x200;
            p_i93->intl_flags |= RW_I93_FLAG_16BIT_NUM_BLOCK;
            break;
        case RW_I93_ONS_N24RF64:
        case RW_I93_ONS_N24RF64E:
            p_i93->num_block = 0x800;
            p_i93->intl_flags |= RW_I93_FLAG_16BIT_NUM_BLOCK;
            break;
        default:
            return false;
    }
    return true;
}

void rw_i93_get_product_version(uint8_t* p_uid)
{
    tT5T_INFO *p_i93 = &s_t5t_info;

    if (!memcmp(p_i93->uid, p_uid, I93_UID_BYTE_LEN)) {
        return;
    }
    NFCLOG_I("enter");

    if (!osi_memcpy(p_i93->uid, I93_UID_BYTE_LEN, p_uid, I93_UID_BYTE_LEN)) {
        NFCLOG_E("memecpy failed");
        return;
    }

    if (p_uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_NXP) {
        if (p_uid[I93_UID_MFG_PROD_TYPE_IND] == I93_UID_ICODE_SLI)
            p_i93->product_version = RW_I93_ICODE_SLI;
        else if (p_uid[I93_UID_MFG_PROD_TYPE_IND] == I93_UID_ICODE_SLI_S)
            p_i93->product_version = RW_I93_ICODE_SLI_S;
        else if (p_uid[I93_UID_MFG_PROD_TYPE_IND] == I93_UID_ICODE_SLI_L)
            p_i93->product_version = RW_I93_ICODE_SLI_L;
        else
            p_i93->product_version = RW_I93_UNKNOWN_PRODUCT;
    } else if (p_uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_TI) {
        get_product_version_ti(p_uid);
    } else if ((p_uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ST) &&
                (p_i93->info_flags & I93_INFO_FLAG_IC_REF)) {
        get_product_version_st();
    } else if ((p_uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ONS) &&
                (p_i93->info_flags & I93_INFO_FLAG_IC_REF)) {
        get_product_version_ons();
    } else {
        p_i93->product_version = RW_I93_UNKNOWN_PRODUCT;
    }

    NFCLOG_I("product_version = <%s>",  rw_i93_get_tag_name(p_i93->product_version));
    switch (p_i93->product_version) {
        case RW_I93_TAG_IT_HF_I_STD_CHIP_INLAY:
        case RW_I93_TAG_IT_HF_I_PRO_CHIP_INLAY:
            /* these don't support Get System Information Command */
            /* these support only Inventory, Stay Quiet, Read Single Block, Write
            * Single Block, Lock Block */
            p_i93->block_size = I93_TAG_IT_HF_I_STD_PRO_CHIP_INLAY_BLK_SIZE;
            p_i93->num_block = I93_TAG_IT_HF_I_STD_PRO_CHIP_INLAY_NUM_USER_BLK;
            break;
        default:
            break;
    }
}

static bool get_ic_reference_in_sys_info(uint16_t length, uint8_t* p, uint8_t* p_uid)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    if ((p_i93->info_flags & I93_INFO_FLAG_IC_REF) != 0) {
        NFCLOG_I("no need get ic reference info");
        return true;
    }
    if (length == 0) {
        NFCLOG_E("invalid length %d", __LINE__);
        return false;
    }
    STREAM_TO_UINT8(p_i93->ic_reference, p);

    /* clear existing UID to set product version */
    p_i93->uid[I93_UID_MSB_IND] = 0x00;

    /* store UID and get product version */
    rw_i93_get_product_version(p_uid);

    if (p_i93->uid[I93_UID_MSB_IND] != I93_UID_MSB) {
        NFCLOG_E("invalid UID MSB");
        return false;
    }

    if ((p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_NXP) &&
        (p_i93->ic_reference == I93_IC_REF_ICODE_SLI_L)) {
        p_i93->num_block = NUM_BLOCK_8;
        p_i93->block_size = BLOCK_SIZE_4;
    } else if (p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ST) {
        /*
        **  LRI1K:      010000xx(b), blockSize: 4, numberBlocks: 0x20
        **  LRI2K:      001000xx(b), blockSize: 4, numberBlocks: 0x40
        **  LRIS2K:     001010xx(b), blockSize: 4, numberBlocks: 0x40
        **  LRIS64K:    010001xx(b), blockSize: 4, numberBlocks: 0x800
        **  M24LR64-R:  001011xx(b), blockSize: 4, numberBlocks: 0x800
        **  M24LR04E-R: 01011010(b), blockSize: 4, numberBlocks: 0x80
        **  M24LR16E-R: 01001110(b), blockSize: 4, numberBlocks: 0x200
        **  M24LR16D-W: 01001101(b), blockSize: 4, numberBlocks: 0x200
        **  M24LR64E-R: 01011110(b), blockSize: 4, numberBlocks: 0x800
        */
        if ((p_i93->product_version == RW_I93_ST_M24LR16E_R) ||
            (p_i93->product_version == RW_I93_ST_M24LR16D_W) ||
            (p_i93->product_version == RW_I93_ST_M24LR64E_R)) {
            /*
            ** M24LR16E-R or M24LR16D-W or M24LR64E-R returns system information
            ** without memory size, if option flag is not set.
            ** LRIS64K and M24LR64-R return error if option flag is not
            ** set.
            */
            if (!(p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK)) {
                /* get memory size with protocol extension flag */
                    /* ST supports more than 2040 bytes */
                    p_i93->intl_flags |= RW_I93_FLAG_16BIT_NUM_BLOCK;
                    NFCLOG_E(" %d invlid flag", __LINE__);
                    return false;
            }
        } else if ((p_i93->product_version == RW_I93_ST_LRI2K) &&
                (p_i93->ic_reference == 0x21)) {
            /* workaround of byte order in memory size information */
            p_i93->num_block = NUM_BLOCK_64;
            p_i93->block_size = BLOCK_SIZE_4;
        } else if (!(p_i93->info_flags & I93_INFO_FLAG_MEM_SIZE)) {
            if (!(p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS)) {
                    /* ST supports more than 2040 bytes */
                    p_i93->intl_flags |= RW_I93_FLAG_EXT_COMMANDS;
                    NFCLOG_E(" %d invlid flag", __LINE__);
                    return false;
            }
        }
    } else if (p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ONS) {
        if (!get_block_status_ons()) {
            return false;
        }
    }

    return true;
}

static bool rw_i93_process_sys_info(uint8_t* p_data, uint16_t length)
{
    uint8_t* p = p_data;
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t uid[I93_UID_BYTE_LEN];
    uint8_t* p_uid = uid;

    NFCLOG_I("enter, length:%d", length);

    if (length < (I93_UID_BYTE_LEN + 1)) return false;

    STREAM_TO_UINT8(p_i93->info_flags, p);

    STREAM_TO_ARRAY(p_uid, p, I93_UID_BYTE_LEN);

    length -= (I93_UID_BYTE_LEN + 1);

    if (p_i93->info_flags & I93_INFO_FLAG_DSFID) {
        if (length == 0) {
            NFCLOG_E("invalid length %d", __LINE__);
            return false;
        }
        STREAM_TO_UINT8(p_i93->dsfid, p);
        length--;
    }
    if (p_i93->info_flags & I93_INFO_FLAG_AFI) {
        if (length == 0) {
            NFCLOG_E("invalid length %d", __LINE__);
            return false;
        }
        STREAM_TO_UINT8(p_i93->afi, p);
        length--;
    }
    if (p_i93->info_flags & I93_INFO_FLAG_MEM_SIZE) {
        bool block_16_bit = p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK;
        if (block_16_bit && length > LENGTH_2) {
            STREAM_TO_UINT16_LE(p_i93->num_block, p);
            length -= LENGTH_2;
        } else if (!block_16_bit && length > 1) {
            STREAM_TO_UINT8(p_i93->num_block, p);
            length--;
        } else {
            NFCLOG_E("invalid length %d", __LINE__);
            return false;
        }
        /* it is one less than actual number of bytes */
        p_i93->num_block += 1;

        STREAM_TO_UINT8(p_i93->block_size, p);
        length--;
        /* it is one less than actual number of blocks */
        p_i93->block_size = (p_i93->block_size & 0x1F) + 1;
    }

    if (!get_ic_reference_in_sys_info(length, p, p_uid)) return false;
    NFCLOG_I("num_block %d, block_size:%d", p_i93->num_block, p_i93->block_size);

    return true;
}

static bool get_ic_reference_in_ext_sys_info(uint16_t length, uint8_t* p, uint8_t* p_uid)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    if (p_i93->info_flags & I93_INFO_FLAG_IC_REF) {
        if (length < I93_INFO_IC_REF_LEN) {
            NFCLOG_E("invalid length %d", __LINE__);
            return false;
        }
        STREAM_TO_UINT8(p_i93->ic_reference, p);
        length--;
        /* clear existing UID to set product version */
        p_i93->uid[I93_UID_MSB_IND] = 0x00;

        /* store UID and get product version */
        rw_i93_get_product_version(p_uid);

        if (p_i93->uid[I93_UID_MSB_IND] == I93_UID_MSB) {
            if ((p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ST) ||
                (p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ONS)) {
                /* ST & ONS supports more than 2040 bytes */
                p_i93->intl_flags |= RW_I93_FLAG_EXT_COMMANDS;
            }
        }
    }

    return true;
}


static bool rw_i93_process_ext_sys_info(uint8_t* p_data, uint16_t length)
{
    uint8_t* p = p_data;
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t uid[I93_UID_BYTE_LEN];
    uint8_t *p_uid;

    NFCLOG_D("enter");

    if (length < (I93_UID_BYTE_LEN + 1)) {
        NFCLOG_E("invalid length %d", __LINE__);
        return false;
    }
    STREAM_TO_UINT8(p_i93->info_flags, p);
    length--;

    p_uid = uid;
    STREAM_TO_ARRAY(p_uid, p, I93_UID_BYTE_LEN);

    length -= I93_UID_BYTE_LEN;

    if (p_i93->info_flags & I93_INFO_FLAG_DSFID) {
        if (length < I93_INFO_DSFID_LEN) {
            NFCLOG_E("invalid length %d", __LINE__);
            return false;
        }
        STREAM_TO_UINT8(p_i93->dsfid, p);
        length--;
    }
    if (p_i93->info_flags & I93_INFO_FLAG_AFI) {
        if (length < I93_INFO_AFI_LEN) {
            NFCLOG_E("invalid length %d", __LINE__);
            return false;
        }
        STREAM_TO_UINT8(p_i93->afi, p);
        length--;
    }
    if (p_i93->info_flags & I93_INFO_FLAG_MEM_SIZE) {
        if (length < I93_INFO_16BIT_NUM_BLOCK_LEN + I93_INFO_BLOCK_SIZE_LEN) {
            NFCLOG_E("invalid length %d", __LINE__);
            return false;
        }

        STREAM_TO_UINT16_LE(p_i93->num_block, p);
        length -= I93_INFO_16BIT_NUM_BLOCK_LEN;
        /* it is one less than actual number of bytes */
        p_i93->num_block += 1;

        STREAM_TO_UINT8(p_i93->block_size, p);
        length--;
        /* it is one less than actual number of blocks */
        p_i93->block_size = (p_i93->block_size & 0x1F) + 1;
    }

    if (!get_ic_reference_in_ext_sys_info(length, p, p_uid)) {
        return false;
    }
    NFCLOG_I("num_block %d, block_size:%d", p_i93->num_block, p_i93->block_size);

    return true;
}

bool rw_i93_check_legacy_product(uint8_t ic_manuf, uint8_t pdt_code)
{
    if (s_t5t_info.mute_legacy) {
        NFCLOG_I("mute legacy enabled");
        return false;
    }
    if (g_dta_mode_enabled) {
        return false;
    }

    NFCLOG_D("IC manufacturer:0x%x, Product code:0x%x", ic_manuf, pdt_code);

    uint8_t pdt_code_family = 0;

    if (ic_manuf == I93_UID_IC_MFG_CODE_NXP) {
        NFCLOG_D("No I93 legacy product detected");
        return false;
    }

    if (ic_manuf == I93_UID_IC_MFG_CODE_ST) {
        pdt_code_family = pdt_code & I93_IC_REF_ST_MASK;
        switch (pdt_code_family) {
            case I93_IC_REF_ST_LRI1K:
            case I93_PROD_CODE_ST_M24LR04E_R_MASK:
            case I93_PROD_CODE_ST_LRI2K_MASK:
            case I93_PROD_CODE_ST_LRIS2K_MASK:
            case I93_PROD_CODE_ST_LRIS64K_MASK:
            case I93_PROD_CODE_ST_M24LR16E_R_MASK:
            case I93_PROD_CODE_ST_M24LR64_R_MASK:
            case I93_PROD_CODE_ST_M24LR64E_R_MASK:
            case I93_PROD_CODE_ST_ST25DV_K_MASK:
                NFCLOG_D("ISO 15693 legacy product detected");
                return true;
            default:
                NFCLOG_D("T5T NFC Forum product detected");
                return false;
        }
    }

    if ((ic_manuf == I93_UID_IC_MFG_CODE_TI) ||
        (ic_manuf == I93_UID_IC_MFG_CODE_ONS)) {
        NFCLOG_D("I93 legacy product detected");
        return true;
    }

    NFCLOG_D("T5T NFC Forum product detected");
    return false;
}

static bool rw_i93_check_sys_info_prot_ext(uint8_t error_code)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    uint8_t *p_rsp_data;
    uint16_t length = 0;
    uint8_t flags = 0;
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;

    if (((p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ST) ||
        (p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ONS)) &&
        (p_i93->sent_cmd == I93_CMD_GET_SYS_INFO) &&
        (error_code == I93_ERROR_CODE_OPTION_NOT_SUPPORTED)) {
        /* getting system info with protocol extension flag */
        /* This ST& ONS tag supports more than 2040 bytes */
        NFCLOG_I("send get sys info cmd with protocol extension flag");
        p_i93->intl_flags |= RW_I93_FLAG_16BIT_NUM_BLOCK;
        reset_nci_buf(p_rsp);
        status = rw_i93_send_cmd_get_sys_info(NULL, I93_FLAG_PROT_EXT_YES, p_rsp);
        if (status != NCI_STATUS_OK) {
            NFCLOG_E("send get sys info cmd failed");
            goto exit;
        }
        length = p_rsp->len;
        if (length == 0) {
            status = NCI_STATUS_EXT_FAILED;
            NFCLOG_E("invalid response length");
            goto exit;
        }
        p_rsp_data = p_rsp->data;
        for (int i = 0; i < p_rsp->len; i++) {
            NFCLOG_I("rsp->data[%d]:0x%02x", i, *(p_rsp_data + i));
        }

        STREAM_TO_UINT8(flags, p_rsp_data);
        length--;
        if (flags & I93_FLAG_ERROR_DETECTED) {
            status = NCI_STATUS_EXT_FAILED;
            NFCLOG_E("error code:0x%02x", *p_rsp_data);
            goto exit;
        }
        status = process_sys_info(p_rsp_data, length);
    }

exit:
    free_nci_buf(p_rsp);
    return status == NCI_STATUS_OK;
}

static tNCI_STATUS get_sys_info_with_prot_ext_flag()
{
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    uint8_t *p_rsp_data;
    uint16_t length = 0;
    uint8_t flags = 0;
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;

    status = rw_i93_send_cmd_get_sys_info(NULL, I93_FLAG_PROT_EXT_YES, p_rsp);
    if (status != NCI_STATUS_OK) {
        NFCLOG_E("send get sys info cmd failed");
        goto exit;
    }
    length = p_rsp->len;
    if (length == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;
    for (int i = 0; i < p_rsp->len; i++) {
        NFCLOG_D("rsp->data[%d]:0x%02x", i, *(p_rsp_data + i));
    }

    STREAM_TO_UINT8(flags, p_rsp_data);
    length--;
    if (flags & I93_FLAG_ERROR_DETECTED) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("error code:0x%02x", *p_rsp_data);
        goto exit;
    }
    if (!rw_i93_process_sys_info(p_rsp_data, length)) {
        NFCLOG_E("process sys info failed");
        status = NCI_STATUS_EXT_FAILED;
        goto exit;
    }

exit:
    free_nci_buf(p_rsp);
    return status;
}

static tNCI_STATUS get_ext_sys_info()
{
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    uint8_t *p_rsp_data;
    uint16_t length = 0;
    uint8_t flags = 0;
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;

    status = rw_i93_send_cmd_get_ext_sys_info(NULL, p_rsp);
    if (status != NCI_STATUS_OK) {
        NFCLOG_E("send get ext sys info cmd failed");
        goto exit;
    }
    length = p_rsp->len;
    if (length == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;
    for (int i = 0; i < p_rsp->len; i++) {
        NFCLOG_D("rsp->data[%d]:0x%02x", i, *(p_rsp_data + i));
    }

    STREAM_TO_UINT8(flags, p_rsp_data);
    length--;
    if (flags & I93_FLAG_ERROR_DETECTED) {
        NFCLOG_E("error code:0x%02x", *p_rsp_data);
        status = NCI_STATUS_EXT_FAILED;
        goto exit;
    }
    if (!rw_i93_process_ext_sys_info(p_rsp_data, length)) {
        NFCLOG_E("process sys info failed");
        status = NCI_STATUS_EXT_FAILED;
        goto exit;
    }
exit:
    free_nci_buf(p_rsp);
    return status;
}


static tNCI_STATUS process_sys_info(uint8_t* p_rsp_data, uint16_t length)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;

    if (!rw_i93_process_sys_info(p_rsp_data, length)) {
        if (p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK) {
            NFCLOG_E("process sys info failed, send get sys info cmd with protocol extension flag");
            status = get_sys_info_with_prot_ext_flag();
        } else if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
            NFCLOG_E("process sys info failed, send get ext sys info cmd");
            status = get_ext_sys_info();
        } else {
            NFCLOG_E("process sys info failed");
            status = NCI_STATUS_EXT_FAILED;
        }
    } else {
        status = NCI_STATUS_OK;
    }

    return status;
}

tNCI_STATUS rw_i93_get_sys_info(uint8_t* p_uid)
{
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    uint8_t *p_rsp_data;
    uint16_t length = 0;
    uint8_t flags = 0;

    if (p_uid) {
        status = rw_i93_send_cmd_get_sys_info(p_uid, I93_FLAG_PROT_EXT_NO, p_rsp);
    } else {
        status = rw_i93_send_cmd_get_sys_info(NULL, I93_FLAG_PROT_EXT_NO, p_rsp);
    }

    if (status != NCI_STATUS_OK) {
        NFCLOG_E("send get sys info cmd failed");
        goto exit;
    }
    length = p_rsp->len;
    if (length == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;

    STREAM_TO_UINT8(flags, p_rsp_data);
    length--;

    if (flags & I93_FLAG_ERROR_DETECTED) {
        if (length && rw_i93_check_sys_info_prot_ext(*p_rsp_data)) {
            status = NCI_STATUS_OK;
        } else {
            status = NCI_STATUS_EXT_FAILED;
            NFCLOG_E("error code:0x%02x", *p_rsp_data);
        }
        goto exit;
    }

    status = process_sys_info(p_rsp_data, length);

exit:
    free_nci_buf(p_rsp);
    return status;
}

tNCI_STATUS rw_i93_send_cmd_get_sys_info(uint8_t* p_uid, uint8_t extra_flags, tNCI_BUF* p_rsp)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_STATUS ret = NCI_STATUS_OK;

    NFCLOG_I("enter");
    tNCI_BUF *p_cmd = alloc_nci_buf(0);

    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        ret = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    /* Flags */
    UINT8_TO_BUF(p_cmd, (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER |
                        RW_I93_FLAG_DATA_RATE | extra_flags));

    /* Command Code */
    UINT8_TO_BUF(p_cmd, I93_CMD_GET_SYS_INFO);

    /* Parameters */
    if (p_uid != NULL) {
        ARRAY_TO_BUF(p_cmd,  p_uid, I93_UID_BYTE_LEN);
    } else {
        ARRAY_TO_BUF(p_cmd,  p_i93->uid, I93_UID_BYTE_LEN);
    }

    ret = rw_transmit(p_cmd, p_rsp);
    if (NCI_STATUS_OK == ret) {
        p_i93->sent_cmd = I93_CMD_GET_SYS_INFO;
    }

    //check response flag outside
exit:
    free_nci_buf(p_cmd);
    return ret;
}

tNCI_STATUS rw_i93_send_cmd_get_ext_sys_info(uint8_t* p_uid, tNCI_BUF* p_rsp)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_STATUS ret = NCI_STATUS_OK;
    NFCLOG_I("enter");

    tNCI_BUF *p_cmd = alloc_nci_buf(0);

    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        ret = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    /* Flags */
    UINT8_TO_BUF(p_cmd, (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER |
                        RW_I93_FLAG_DATA_RATE));

    /* Command Code */
    UINT8_TO_BUF(p_cmd, I93_CMD_EXT_GET_SYS_INFO);

    /* Parameters request field */
    UINT8_TO_BUF(p_cmd,
        (I93_INFO_FLAG_MOI | I93_INFO_FLAG_DSFID | I93_INFO_FLAG_AFI | I93_INFO_FLAG_MEM_SIZE | I93_INFO_FLAG_IC_REF));

    /* Parameters */
    if (p_uid != NULL) {
        ARRAY_TO_BUF(p_cmd, p_uid, I93_UID_BYTE_LEN);
    } else {
        ARRAY_TO_BUF(p_cmd, p_i93->uid, I93_UID_BYTE_LEN);
    }

    ret = rw_transmit(p_cmd, p_rsp);
    if (NCI_STATUS_OK == ret) {
        s_t5t_info.sent_cmd = I93_CMD_GET_SYS_INFO;
    }

    //check respone flag outside
exit:

    free_nci_buf(p_cmd);
    return ret;
}

void rw_i93_send_cmd_set_addressing_mode(bool mode)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    NFCLOG_D("enter, mode:%d", mode);
    if (mode) {
        p_i93->addr_mode = RW_I93_MODE_ADDRESSED;
    } else {
        p_i93->addr_mode = RW_I93_MODE_NON_ADDRESSED;
    }
}

tNCI_STATUS rw_i93_send_cmd_stay_quiet(const uint8_t* p_uid)
{
    tNCI_BUF *p_cmd;
    uint8_t flags = 0;
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;
    NFCLOG_D("enter");
    p_cmd = alloc_nci_buf(0);
    if (!p_cmd) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }
    if (!p_uid) {
        NFCLOG_E("invlid uid");
        goto exit;
    }
    /* Flags */
    flags = (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE);
    UINT8_TO_BUF(p_cmd, flags);
    /* Command Code */
    UINT8_TO_BUF(p_cmd, I93_CMD_STAY_QUIET);

    ARRAY_TO_BUF(p_cmd, p_uid, I93_UID_BYTE_LEN);

    status = rf_transmit_async(p_cmd);
    if (status != NCI_STATUS_OK) {
        NFCLOG_E("send rw_i93_send_cmd_stay_quiet cmd failed");
        goto exit;
    }

    p_i93->sent_cmd = I93_CMD_STAY_QUIET;
exit:
    free_nci_buf(p_cmd);
    NFCLOG_D("exit");
    return status;
}

tNCI_STATUS rw_i93_send_cmd_select(const uint8_t* p_uid)
{
    tNCI_BUF *p_cmd;
    tNCI_BUF *p_rsp;
    uint8_t flags = 0;
    uint8_t rsp_flags = 0;
    uint16_t length = 0;
    uint8_t *p_rsp_data;
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;
    NFCLOG_D("enter");
    p_cmd = alloc_nci_buf(0);
    p_rsp = alloc_nci_buf(0);
    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    if (!p_uid) {
        NFCLOG_E("invlid uid");
        goto exit;
    }
    /* Flags */
    flags = (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE);
    UINT8_TO_BUF(p_cmd, flags);
    /* Command Code */
    UINT8_TO_BUF(p_cmd, I93_CMD_SELECT);

    ARRAY_TO_BUF(p_cmd, p_uid, I93_UID_BYTE_LEN);

    status = rw_transmit(p_cmd, p_rsp);
    if (status != NCI_STATUS_OK) {
        NFCLOG_E("send select cmd failed");
        goto exit;
    }

    length = p_rsp->len;
    if (length == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;

    STREAM_TO_UINT8(rsp_flags, p_rsp_data);

    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("get error flag:0x%02x", rsp_flags);
        goto exit;
    }
    p_i93->sent_cmd = I93_CMD_SELECT;
exit:
    free_nci_buf(p_cmd);
    free_nci_buf(p_rsp);
    NFCLOG_D("exit");
    return status;
}

static void assemble_cmd_inventory(tNCI_BUF *p_cmd, uint8_t* p_uid, bool including_afi, uint8_t afi)
{
    uint8_t flags = 0;

    /* Flags */
    flags = (I93_FLAG_SLOT_ONE | I93_FLAG_INVENTORY_SET |
        RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE);
    if (including_afi) {
        flags |= I93_FLAG_AFI_PRESENT;
    }
    UINT8_TO_BUF(p_cmd, flags);

    /* Command Code */
    UINT8_TO_BUF(p_cmd, I93_CMD_INVENTORY);

    if (including_afi) {
        /* Parameters */
        UINT8_TO_BUF(p_cmd, afi); /* Optional AFI */
    }
    if (p_uid) {
        UINT8_TO_BUF(p_cmd, I93_UID_BYTE_LEN * BITS_PER_BYTE); /* Mask Length */
        ARRAY_TO_BUF(p_cmd, p_uid, I93_UID_BYTE_LEN);        /* UID */
    } else {
        UINT8_TO_BUF(p_cmd, 0x00); /* Mask Length */
    }
}

tNCI_STATUS rw_i93_send_cmd_inventory(uint8_t* p_uid, bool including_afi, uint8_t afi)
{
    tNCI_BUF *p_cmd = alloc_nci_buf(0);
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    uint8_t rsp_flags = 0;
    uint16_t length = 0;
    uint8_t *p_rsp_data;
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;

    NFCLOG_D("including_afi:%d, AFI:0x%02X", including_afi, afi);
    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    assemble_cmd_inventory(p_cmd, p_uid, including_afi, afi);

    status = rw_transmit(p_cmd, p_rsp);
    if (status != NCI_STATUS_OK) {
        NFCLOG_E("send inventroy cmd failed");
        goto exit;
    }
    length = p_rsp->len;
    if (length == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;

    STREAM_TO_UINT8(rsp_flags, p_rsp_data);
    length--;

    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("error code:0x%02x", *p_rsp_data);
        goto exit;
    }
    if (length < I93_INFO_DSFID_LEN + I93_UID_BYTE_LEN) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid length:%d, %d", length, __LINE__);
        goto exit;
    }
    STREAM_TO_UINT8(p_i93->dsfid, p_rsp_data);

    STREAM_TO_ARRAY(p_i93->uid, p_rsp_data, I93_UID_BYTE_LEN)

    /* store UID and get product version */
    rw_i93_get_product_version(p_i93->uid);
    p_i93->sent_cmd = I93_CMD_INVENTORY;

exit:
    free_nci_buf(p_cmd);
    free_nci_buf(p_rsp);
    return status;
}

static void assemble_cmd_read_single_block(tNCI_BUF *p_cmd, uint32_t block_number, bool read_security)
{
    uint8_t flags = 0;
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t *p_t5t_info_uid = p_i93->uid;

    /* Flags */
    if (p_i93->addr_mode == RW_I93_MODE_ADDRESSED) {
        flags = (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER |
               RW_I93_FLAG_DATA_RATE);
    } else {
        flags = (RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE);

        if (p_i93->intl_flags & RW_I93_FLAG_SELECTED_STATE) {
            flags |= I93_FLAG_SELECT_SET;
        }
    }

    if (read_security) flags |= I93_FLAG_OPTION_SET;

    if (p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK)
        flags |= I93_FLAG_PROT_EXT_YES;

    UINT8_TO_BUF(p_cmd, flags);
    /* Command Code */
    if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
        UINT8_TO_BUF(p_cmd, I93_CMD_EXT_READ_SINGLE_BLOCK);
    } else {
        UINT8_TO_BUF(p_cmd, I93_CMD_READ_SINGLE_BLOCK);
    }
    /* Parameters */
    if (flags & I93_FLAG_ADDRESS_SET) {
        ARRAY_TO_BUF(p_cmd, p_t5t_info_uid, I93_UID_BYTE_LEN);      /* UID */
    }

    if ((p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK)||
        (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS)) {
        UINT16_TO_BUF_LE(p_cmd, block_number); /* Block number */
    } else {
        UINT8_TO_BUF(p_cmd, block_number); /* Block number */
    }
}

tNCI_STATUS rw_i93_send_cmd_read_single_block(uint32_t block_number, bool read_security, tNCI_BUF* p_rsp)
{
    uint8_t rsp_flags = 0;
    uint8_t *p_rsp_data;
    tNCI_BUF *p_cmd = alloc_nci_buf(0);
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;
    NFCLOG_D("enter");

    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    assemble_cmd_read_single_block(p_cmd, block_number, read_security);

    status = rw_transmit(p_cmd, p_rsp);
    if (NCI_STATUS_OK == status) {
        if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
            p_i93->sent_cmd = I93_CMD_EXT_READ_SINGLE_BLOCK;
        } else {
            p_i93->sent_cmd = I93_CMD_READ_SINGLE_BLOCK;
        }
    } else {
        NFCLOG_E("rw transmi failed");
        goto exit;
    }
    if (p_rsp->len == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;
    for (int i = 0; i < p_rsp->len; i++) {
        NFCLOG_I("rsp->data[%d]:0x%02x", i, *(p_rsp_data + i));
    }
    STREAM_TO_UINT8(rsp_flags, p_rsp_data);
    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("error code:0x%02x", *p_rsp_data);
        goto exit;
    }
exit:
    NFCLOG_D("exit");
    free_nci_buf(p_cmd);
    return status;
}

#if IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)

static char* rw_i93_get_tag_name(uint8_t product_version)
{
    static char* tag_name[RW_I93_UNKNOWN_PRODUCT + 1];
    tag_name[RW_I93_ICODE_SLI] = "SLI/SLIX";
    tag_name[RW_I93_ICODE_SLI_S] = "SLI-S/SLIX-S";
    tag_name[RW_I93_ICODE_SLI_L] = "SLI-L/SLIX-L";
    tag_name[RW_I93_TAG_IT_HF_I_PLUS_INLAY] = "Tag-it HF-I Plus Inlay";
    tag_name[RW_I93_TAG_IT_HF_I_PLUS_CHIP] = "Tag-it HF-I Plus Chip";
    tag_name[RW_I93_TAG_IT_HF_I_STD_CHIP_INLAY] = "Tag-it HF-I Standard Chip/Inlyas";
    tag_name[RW_I93_TAG_IT_HF_I_PRO_CHIP_INLAY] = "Tag-it HF-I Pro Chip/Inlays";
    tag_name[RW_I93_ST_LRI1K] = "LRi1K";
    tag_name[RW_I93_ST_LRI2K] = "LRi2K";
    tag_name[RW_I93_ST_LRIS2K] = "LRiS2K";
    tag_name[RW_I93_ST_LRIS64K] = "LRiS64K";
    tag_name[RW_I93_ST_M24LR64_R] = "M24LR64";
    tag_name[RW_I93_ST_M24LR04E_R] = "M24LR04E";
    tag_name[RW_I93_ST_M24LR16E_R] = "M24LR16E";
    tag_name[RW_I93_ST_M24LR16D_W] = "M24LR16D-W";
    tag_name[RW_I93_ST_M24LR64E_R] = "M24LR64E";
    tag_name[RW_I93_ST_ST25DV04K] = "ST25DV04";
    tag_name[RW_I93_ST_ST25DVHIK] = "ST25DV";
    tag_name[RW_I93_ONS_N36RW02] = "N36RW02";
    tag_name[RW_I93_ONS_N24RF04] = "N24RF04";
    tag_name[RW_I93_ONS_N24RF04E] = "N24RF04E";
    tag_name[RW_I93_ONS_N24RF16] = "N24RF16";
    tag_name[RW_I93_ONS_N24RF16E] = "N24RF16E";
    tag_name[RW_I93_ONS_N24RF64] = "N24RF64";
    tag_name[RW_I93_ONS_N24RF64E] = "N24RF64E";
    tag_name[RW_I93_UNKNOWN_PRODUCT] = "UNKNOWN";

    if (product_version >= RW_I93_UNKNOWN_PRODUCT) {
        return "UNKNOWN";
    } else {
        return tag_name[product_version];
    }
}

static void get_product_version_st()
{
    tT5T_INFO *p_i93 = &s_t5t_info;

    if (p_i93->ic_reference == I93_IC_REF_ST_M24LR04E_R)
        p_i93->product_version = RW_I93_ST_M24LR04E_R;
    else if (p_i93->ic_reference == I93_IC_REF_ST_M24LR16E_R)
        p_i93->product_version = RW_I93_ST_M24LR16E_R;
    else if (p_i93->ic_reference == I93_IC_REF_ST_M24LR16D_W)
        p_i93->product_version = RW_I93_ST_M24LR16D_W;
    else if (p_i93->ic_reference == I93_IC_REF_ST_M24LR64E_R)
        p_i93->product_version = RW_I93_ST_M24LR64E_R;
    else if (p_i93->ic_reference == I93_IC_REF_ST_ST25DVHIK)
        p_i93->product_version = RW_I93_ST_ST25DVHIK;
    else if (p_i93->ic_reference == I93_IC_REF_ST_ST25DV04K)
        p_i93->product_version = RW_I93_ST_ST25DV04K;
    else {
        switch (p_i93->ic_reference & I93_IC_REF_ST_MASK) {
            case I93_IC_REF_ST_LRI1K:
                p_i93->product_version = RW_I93_ST_LRI1K;
                break;
            case I93_IC_REF_ST_LRI2K:
                p_i93->product_version = RW_I93_ST_LRI2K;
                break;
            case I93_IC_REF_ST_LRIS2K:
                p_i93->product_version = RW_I93_ST_LRIS2K;
                break;
            case I93_IC_REF_ST_LRIS64K:
                p_i93->product_version = RW_I93_ST_LRIS64K;
                break;
            case I93_IC_REF_ST_M24LR64_R:
                p_i93->product_version = RW_I93_ST_M24LR64_R;
                break;
            default:
                p_i93->product_version = RW_I93_UNKNOWN_PRODUCT;
                break;
        }
    }
}

static void get_product_version_ons()
{
    tT5T_INFO *p_i93 = &s_t5t_info;

    switch (p_i93->ic_reference) {
        case I93_IC_REF_ONS_N36RW02:
            p_i93->product_version = RW_I93_ONS_N36RW02;
            break;
        case I93_IC_REF_ONS_N24RF04:
            p_i93->product_version = RW_I93_ONS_N24RF04;
            break;
        case I93_IC_REF_ONS_N24RF04E:
            p_i93->product_version = RW_I93_ONS_N24RF04E;
            break;
        case I93_IC_REF_ONS_N24RF16:
            p_i93->product_version = RW_I93_ONS_N24RF16;
            break;
        case I93_IC_REF_ONS_N24RF16E:
            p_i93->product_version = RW_I93_ONS_N24RF16E;
            break;
        case I93_IC_REF_ONS_N24RF64:
            p_i93->product_version = RW_I93_ONS_N24RF64;
            break;
        case I93_IC_REF_ONS_N24RF64E:
            p_i93->product_version = RW_I93_ONS_N24RF64E;
            break;
        default:
            p_i93->product_version = RW_I93_UNKNOWN_PRODUCT;
            break;
        }
}

static void get_product_version_ti(uint8_t* p_uid)
{
    tT5T_INFO *p_i93 = &s_t5t_info;

    if ((p_uid[I93_UID_MFG_PROD_TYPE_IND] & I93_UID_TAG_IT_HF_I_PRODUCT_ID_MASK) == I93_UID_TAG_IT_HF_I_PLUS_INLAY)
        p_i93->product_version = RW_I93_TAG_IT_HF_I_PLUS_INLAY;
    else if ((p_uid[I93_UID_MFG_PROD_TYPE_IND] & I93_UID_TAG_IT_HF_I_PRODUCT_ID_MASK) ==
                I93_UID_TAG_IT_HF_I_PLUS_CHIP)
        p_i93->product_version = RW_I93_TAG_IT_HF_I_PLUS_CHIP;
    else if ((p_uid[I93_UID_MFG_PROD_TYPE_IND] & I93_UID_TAG_IT_HF_I_PRODUCT_ID_MASK) ==
            I93_UID_TAG_IT_HF_I_STD_CHIP_INLAY)
        p_i93->product_version = RW_I93_TAG_IT_HF_I_STD_CHIP_INLAY;
    else if ((p_uid[I93_UID_MFG_PROD_TYPE_IND] & I93_UID_TAG_IT_HF_I_PRODUCT_ID_MASK) ==
            I93_UID_TAG_IT_HF_I_PRO_CHIP_INLAY)
        p_i93->product_version = RW_I93_TAG_IT_HF_I_PRO_CHIP_INLAY;
    else
        p_i93->product_version = RW_I93_UNKNOWN_PRODUCT;
}

static void assemble_cmd_lock_block(tNCI_BUF *p_cmd, uint32_t block_number)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t *p_t5t_info_uid = p_i93->uid;
    uint8_t flags = 0;

    /* Flags */
    if ((p_i93->product_version == RW_I93_TAG_IT_HF_I_PLUS_INLAY) ||
        (p_i93->product_version == RW_I93_TAG_IT_HF_I_PLUS_CHIP) ||
        (p_i93->product_version == RW_I93_TAG_IT_HF_I_STD_CHIP_INLAY) ||
        (p_i93->product_version == RW_I93_TAG_IT_HF_I_PRO_CHIP_INLAY)) {
        /* Option must be set for TI tag */
        UINT8_TO_BUF(p_cmd, (I93_FLAG_ADDRESS_SET | I93_FLAG_OPTION_SET |
                            RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE));
    } else {
        if (p_i93->addr_mode == RW_I93_MODE_ADDRESSED) {
            /* Address Mode Selector must be set */
            flags = I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER |
                    RW_I93_FLAG_DATA_RATE;
        } else {
            flags = RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE;

            if (p_i93->intl_flags & RW_I93_FLAG_SELECTED_STATE)
            flags |= I93_FLAG_SELECT_SET;
        }
        if (p_i93->intl_flags & RW_I93_FLAG_SPECIAL_FRAME) {
            /* Option Flag bit must be set */
            flags |= I93_FLAG_OPTION_SET;
        }
        UINT8_TO_BUF(p_cmd, flags);
    }
    /* Command Code */
    if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
        UINT8_TO_BUF(p_cmd, I93_CMD_EXT_LOCK_BLOCK);
    } else {
        UINT8_TO_BUF(p_cmd, I93_CMD_LOCK_BLOCK);
    }
    /* Parameters */
    if (p_i93->addr_mode == RW_I93_MODE_ADDRESSED)
        ARRAY_TO_BUF(p_cmd, p_t5t_info_uid, I93_UID_BYTE_LEN);      /* UID */

    if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
        UINT16_TO_BUF_LE(p_cmd, block_number); /* Block number */
    } else {
        UINT8_TO_BUF(p_cmd, block_number); /* Block number */
    }
}

tNCI_STATUS rw_i93_send_cmd_lock_block(uint32_t block_number)
{
    tT5T_INFO *p_i93 = &s_t5t_info;

    uint8_t rsp_flags = 0;
    uint8_t *p_rsp_data;
    tNCI_BUF *p_cmd = alloc_nci_buf(0);
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;
    NFCLOG_D("enter, block number(%d)", block_number);

    if (!p_cmd) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    assemble_cmd_lock_block(p_cmd, block_number);

    status = rw_transmit(p_cmd, p_rsp);
    if (NCI_STATUS_OK == status) {
        if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS)
            p_i93->sent_cmd = I93_CMD_EXT_LOCK_BLOCK;
        else
            p_i93->sent_cmd = I93_CMD_LOCK_BLOCK;
    }

    if (p_rsp->len == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;
    STREAM_TO_UINT8(rsp_flags, p_rsp_data);

    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        if (((p_i93->product_version == RW_I93_TAG_IT_HF_I_PLUS_INLAY) ||
            (p_i93->product_version == RW_I93_TAG_IT_HF_I_PLUS_CHIP) ||
            (p_i93->product_version == RW_I93_TAG_IT_HF_I_STD_CHIP_INLAY) ||
            (p_i93->product_version == RW_I93_TAG_IT_HF_I_PRO_CHIP_INLAY)) &&
            (*p_rsp_data == I93_ERROR_CODE_BLOCK_FAIL_TO_WRITE)) {
            /* ignore error */
            NFCLOG_I("ignore error %02x", *p_rsp_data);
            status = NCI_STATUS_OK;
        } else {
            NFCLOG_E("error code:0x%02x", *p_rsp_data);
            status = NCI_STATUS_EXT_FAILED;
        }
    }

exit:
    free_nci_buf(p_cmd);
    free_nci_buf(p_rsp);
    NFCLOG_D("exit");
    return status;
}

static void assemble_cmd_read_multi_blocks(tNCI_BUF *p_cmd, uint32_t first_block_number, uint32_t number_blocks)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t *p_t5t_info_uid = p_i93->uid;
    uint8_t flags = 0;

    /* Flags */
    if (p_i93->addr_mode == RW_I93_MODE_ADDRESSED) {
        flags = (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER |
                RW_I93_FLAG_DATA_RATE);
    } else {
        flags = (RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE);

        if (p_i93->intl_flags & RW_I93_FLAG_SELECTED_STATE)
        flags |= I93_FLAG_SELECT_SET;
    }

    if (p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK) {
        flags |= I93_FLAG_PROT_EXT_YES;
    }
    UINT8_TO_BUF(p_cmd, flags);

    /* Command Code */
    if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
        UINT8_TO_BUF(p_cmd, I93_CMD_EXT_READ_MULTI_BLOCK);
    } else {
        UINT8_TO_BUF(p_cmd, I93_CMD_READ_MULTI_BLOCK);
    }

    /* Parameters */
    if (flags & I93_FLAG_ADDRESS_SET) {
        ARRAY_TO_BUF(p_cmd, p_t5t_info_uid, I93_UID_BYTE_LEN);      /* UID */
    }

    if ((p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK) ||
        (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS)) {
        UINT16_TO_BUF_LE(p_cmd, first_block_number); /* First block number */
    } else {
        UINT8_TO_BUF(p_cmd, first_block_number); /* First block number */
    }

    if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
        UINT16_TO_BUF_LE(p_cmd, number_blocks - 1); /* Number of blocks, 0x00 to read one block */
    } else {
        UINT8_TO_BUF(p_cmd, number_blocks - 1); /* Number of blocks, 0x00 to read one block */
    }
}

tNCI_STATUS rw_i93_send_cmd_read_multi_blocks(uint32_t first_block_number, uint32_t number_blocks, tNCI_BUF* p_rsp)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t rsp_flags = 0;
    uint8_t *p_rsp_data;
    tNCI_BUF *p_cmd = alloc_nci_buf(0);
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;
    NFCLOG_D("enter");

    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    assemble_cmd_read_multi_blocks(p_cmd, first_block_number, number_blocks);

    status = rw_transmit(p_cmd, p_rsp);
    if (NCI_STATUS_OK == status) {
        if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
            p_i93->sent_cmd = I93_CMD_EXT_READ_MULTI_BLOCK;
        } else {
            p_i93->sent_cmd = I93_CMD_READ_MULTI_BLOCK;
        }
    } else {
        NFCLOG_E("rw transmi failed");
        goto exit;
    }
    if (p_rsp->len == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;

    STREAM_TO_UINT8(rsp_flags, p_rsp_data);
    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("error code:0x%02x", *p_rsp_data);
    }
exit:
    free_nci_buf(p_cmd);
    NFCLOG_D("exit");
    return status;
}

static void assemble_cmd_get_multi_block_sec(tNCI_BUF *p_cmd, uint32_t first_block_number, uint32_t number_blocks)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t *p_t5t_info_uid = p_i93->uid;
    uint8_t flags = 0;

    /* Flags */
    flags = (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE);

    if (p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK)
        flags |= I93_FLAG_PROT_EXT_YES;

    UINT8_TO_BUF(p_cmd, flags);

    /* Command Code */
    if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
        UINT8_TO_BUF(p_cmd, I93_CMD_EXT_GET_MULTI_BLK_SEC);
    } else {
        UINT8_TO_BUF(p_cmd, I93_CMD_GET_MULTI_BLK_SEC);
    }

    /* Parameters */
    ARRAY_TO_BUF(p_cmd, p_t5t_info_uid, I93_UID_BYTE_LEN);      /* UID */

    if ((p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK) ||
        (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS)) {
        UINT16_TO_BUF_LE(p_cmd, first_block_number); /* First block number */
        UINT16_TO_BUF_LE(p_cmd, number_blocks - 1); /* Number of blocks, 0x00 to read one block */
    } else {
        UINT8_TO_BUF(p_cmd, first_block_number); /* First block number */
        UINT8_TO_BUF(p_cmd, number_blocks - 1); /* Number of blocks, 0x00 to read one block */
    }
}

tNCI_STATUS rw_i93_send_cmd_get_multi_block_sec(uint32_t first_block_number, uint32_t number_blocks, tNCI_BUF* p_rsp)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t rsp_flags = 0;
    uint8_t *p_rsp_data;
    tNCI_BUF *p_cmd = alloc_nci_buf(0);
    tNCI_STATUS status = NCI_STATUS_EXT_FAILED;
    NFCLOG_D("enter");

    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    assemble_cmd_get_multi_block_sec(p_cmd, first_block_number, number_blocks);

    status = rw_transmit(p_cmd, p_rsp);
    if (NCI_STATUS_OK == status) {
        if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
            p_i93->sent_cmd = I93_CMD_EXT_GET_MULTI_BLK_SEC;
        } else {
            p_i93->sent_cmd = I93_CMD_EXT_GET_MULTI_BLK_SEC;
        }
    } else {
        NFCLOG_E("rw transmi failed");
        goto exit;
    }
    if (p_rsp->len == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;
    for (int i = 0; i < p_rsp->len; i++) {
        NFCLOG_I("rsp->data[%d]:0x%02x", i, *(p_rsp_data + i));
    }
    STREAM_TO_UINT8(rsp_flags, p_rsp_data);
    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("error code:0x%02x", *p_rsp_data);
    }
exit:
    free_nci_buf(p_cmd);
    NFCLOG_D("exit");
    return status;
}

tNCI_STATUS rw_i93_send_cmd_write_dsfid(uint8_t dsfid)
{
    tNCI_BUF *p_cmd = alloc_nci_buf(0);
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    uint8_t rsp_flags = 0;
    uint8_t *p_rsp_data;
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t* p_t5t_info_uid = p_i93->uid;
    tNCI_STATUS status = NCI_STATUS_EXT_ALLOC_FAILED;

    NFCLOG_D("enter");

    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    /* Flags */
    UINT8_TO_BUF(p_cmd, (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER |
                        RW_I93_FLAG_DATA_RATE));
    /* Command Code */
    UINT8_TO_BUF(p_cmd, I93_CMD_WRITE_DSFID);
    /* Parameters */
    ARRAY_TO_BUF(p_cmd, p_t5t_info_uid, I93_UID_BYTE_LEN);      /* UID */
    UINT8_TO_BUF(p_cmd, dsfid);              /* DSFID */
    status = rw_transmit(p_cmd, p_rsp);
    if (status != NCI_STATUS_OK) {
        NFCLOG_E("send inventroy cmd failed");
        goto exit;
    }

    if (p_rsp->len == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;
    for (int i = 0; i < p_rsp->len; i++) {
        NFCLOG_I("rsp->data[%d]:0x%02x", i, *(p_rsp_data + i));
    }

    STREAM_TO_UINT8(rsp_flags, p_rsp_data);

    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("error code:0x%02x", *p_rsp_data);
        goto exit;
    }
exit:
    free_nci_buf(p_cmd);
    free_nci_buf(p_rsp);
    return status;
}

tNCI_STATUS rw_i93_send_cmd_write_afi(uint8_t afi)
{
    tNCI_BUF *p_cmd = alloc_nci_buf(0);
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    uint8_t rsp_flags = 0;
    uint8_t *p_rsp_data;
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t* p_t5t_info_uid = p_i93->uid;
    tNCI_STATUS status = NCI_STATUS_EXT_ALLOC_FAILED;

    NFCLOG_D("enter");

    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        status = NCI_STATUS_EXT_ALLOC_FAILED;
        goto exit;
    }

    /* Flags */
    UINT8_TO_BUF(p_cmd, (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER |
                        RW_I93_FLAG_DATA_RATE));

    /* Command Code */
    UINT8_TO_BUF(p_cmd, I93_CMD_WRITE_AFI);
    /* Parameters */
    ARRAY_TO_BUF(p_cmd, p_t5t_info_uid, I93_UID_BYTE_LEN);      /* UID */
    UINT8_TO_BUF(p_cmd, afi);              /* DSFID */

    status = rw_transmit(p_cmd, p_rsp);
    if (status != NCI_STATUS_OK) {
        NFCLOG_E("send inventroy cmd failed");
        goto exit;
    }
    if (p_rsp->len == 0) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length");
        goto exit;
    }
    p_rsp_data = p_rsp->data;
    for (int i = 0; i < p_rsp->len; i++) {
        NFCLOG_I("rsp->data[%d]:0x%02x", i, *(p_rsp_data + i));
    }

    STREAM_TO_UINT8(rsp_flags, p_rsp_data);

    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("error code:0x%02x", *p_rsp_data);
        goto exit;
    }
exit:
    free_nci_buf(p_cmd);
    free_nci_buf(p_rsp);
    return status;
}

static void assemble_cmd_write_single_block(tNCI_BUF *p_cmd, uint32_t block_number,  uint8_t* p_data)
{
    uint8_t flags = 0;
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint8_t *p_t5t_info_uid = p_i93->uid;

    /* Flags */
    if ((p_i93->product_version == RW_I93_TAG_IT_HF_I_PLUS_INLAY) ||
        (p_i93->product_version == RW_I93_TAG_IT_HF_I_PLUS_CHIP) ||
        (p_i93->product_version == RW_I93_TAG_IT_HF_I_STD_CHIP_INLAY) ||
        (p_i93->product_version == RW_I93_TAG_IT_HF_I_PRO_CHIP_INLAY)) {
        /* Option must be set for TI tag */
        flags = (I93_FLAG_ADDRESS_SET | I93_FLAG_OPTION_SET |
                RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE);
    } else {
        if (p_i93->addr_mode == RW_I93_MODE_ADDRESSED) {
            flags = (I93_FLAG_ADDRESS_SET | RW_I93_FLAG_SUB_CARRIER |
                    RW_I93_FLAG_DATA_RATE);
        } else {
            flags = (RW_I93_FLAG_SUB_CARRIER | RW_I93_FLAG_DATA_RATE);
            if (p_i93->intl_flags & RW_I93_FLAG_SELECTED_STATE)
            flags |= I93_FLAG_SELECT_SET;
        }

        if (p_i93->intl_flags & RW_I93_FLAG_SPECIAL_FRAME) {
            /* Option Flag bit must be set */
            flags |= I93_FLAG_OPTION_SET;
        }
    }

    if (p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK)
        flags |= I93_FLAG_PROT_EXT_YES;

    UINT8_TO_BUF(p_cmd, flags);

    /* Command Code */
    if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS) {
        UINT8_TO_BUF(p_cmd, I93_CMD_EXT_WRITE_SINGLE_BLOCK);
    } else {
        UINT8_TO_BUF(p_cmd, I93_CMD_WRITE_SINGLE_BLOCK);
    }

    /* Parameters */
    if (flags & I93_FLAG_ADDRESS_SET) {
        ARRAY_TO_BUF(p_cmd, p_t5t_info_uid, I93_UID_BYTE_LEN);      /* UID */
    }

    if ((p_i93->intl_flags & RW_I93_FLAG_16BIT_NUM_BLOCK) ||
        (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS)) {
        UINT16_TO_BUF_LE(p_cmd, block_number); /* Block number */
    } else {
        UINT8_TO_BUF(p_cmd, block_number); /* Block number */
    }

    /* Data */
    for (int i = 0; i < p_i93->block_size; i ++) {
        UINT8_TO_BUF(p_cmd, *(p_data + i));
    }
}

tNCI_STATUS rw_i93_send_cmd_write_single_block(uint32_t block_number,  uint8_t* p_data)
{
    uint8_t rsp_flags = 0;
    uint8_t *p_rsp_data;
    tNCI_BUF *p_cmd = alloc_nci_buf(0);
    tNCI_BUF *p_rsp = alloc_nci_buf(0);
    tT5T_INFO *p_i93 = &s_t5t_info;
    tNCI_STATUS status = NCI_STATUS_EXT_ALLOC_FAILED;
    NFCLOG_D("enter");

    if (!p_cmd || !p_rsp) {
        NFCLOG_E("invlid buf");
        goto exit;
    }
    assemble_cmd_write_single_block(p_cmd, block_number, p_data);

    status = rw_transmit(p_cmd, p_rsp);
    if (NCI_STATUS_OK == status) {
        if (p_i93->intl_flags & RW_I93_FLAG_EXT_COMMANDS)
            p_i93->sent_cmd = I93_CMD_EXT_WRITE_SINGLE_BLOCK;
        else
            p_i93->sent_cmd = I93_CMD_WRITE_SINGLE_BLOCK;
    } else {
        goto exit;
    }
    if (p_rsp->len == 0 || p_i93->block_size > I93_MAX_BLOCK_LENGH) {
        status = NCI_STATUS_EXT_FAILED;
        NFCLOG_E("invalid response length or block size");
        goto exit;
    }
    p_rsp_data = p_rsp->data;

    STREAM_TO_UINT8(rsp_flags, p_rsp_data);
    if (rsp_flags & I93_FLAG_ERROR_DETECTED) {
        if (((p_i93->product_version == RW_I93_TAG_IT_HF_I_PLUS_INLAY)
            || (p_i93->product_version == RW_I93_TAG_IT_HF_I_PLUS_CHIP)
            || (p_i93->product_version == RW_I93_TAG_IT_HF_I_STD_CHIP_INLAY)
            || (p_i93->product_version == RW_I93_TAG_IT_HF_I_PRO_CHIP_INLAY))
            && (*p_rsp_data == I93_ERROR_CODE_BLOCK_FAIL_TO_WRITE)) {
            /* ignore error */
            NFCLOG_E("ignore error code:0x%02x", *p_rsp_data);
            status = NCI_STATUS_OK;
        } else {
            NFCLOG_E("error code:0x%02x", *p_rsp_data);
            status = NCI_STATUS_EXT_FAILED;
        }
    } else {
        status = NCI_STATUS_OK;
    }

exit:
    NFCLOG_D("exit");
    free_nci_buf(p_rsp);
    free_nci_buf(p_cmd);
    return status;
}

tNCI_STATUS rw_i93_get_next_block_sec(tNCI_BUF* p_rsp)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint32_t num_blocks;
    NFCLOG_D("enter");
    if (p_i93->num_block <= p_i93->rw_offset) {
        NFCLOG_E("rw_offset(0x%x) must be less than num_block(0x%x)", p_i93->rw_offset, p_i93->num_block);
        return NCI_STATUS_EXT_FAILED;
    }
    num_blocks = p_i93->num_block - p_i93->rw_offset;
    if (num_blocks > RW_I93_GET_MULTI_BLOCK_SEC_SIZE)
        num_blocks = RW_I93_GET_MULTI_BLOCK_SEC_SIZE;

    NFCLOG_D("%s intl_flags=%d", __func__, p_i93->intl_flags);
    return rw_i93_send_cmd_get_multi_block_sec(p_i93->rw_offset, num_blocks, p_rsp);
}

static uint32_t recalculate_num_block_ones(uint32_t first_block, uint32_t num_block)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    /* N24RF04, N24RF04E, N24RF16, N24RF16E, N24RF64, N24RF64E requires
    ** - The max number of blocks is 32 and they are all located in the
    **   same sector.
    ** - The sector is 32 blocks of 4 bytes.
    */
    if ((p_i93->product_version == RW_I93_ONS_N36RW02) ||
        (p_i93->product_version == RW_I93_ONS_N24RF04) ||
        (p_i93->product_version == RW_I93_ONS_N24RF04E) ||
        (p_i93->product_version == RW_I93_ONS_N24RF16) ||
        (p_i93->product_version == RW_I93_ONS_N24RF16E) ||
        (p_i93->product_version == RW_I93_ONS_N24RF64) ||
        (p_i93->product_version == RW_I93_ONS_N24RF64E)) {
        if (num_block > I93_ONS_MAX_BLOCKS_PER_READ)
            num_block = I93_ONS_MAX_BLOCKS_PER_READ;
        if ((first_block / I93_ONS_BLOCKS_PER_SECTOR) !=
            ((first_block + num_block - 1) / I93_ONS_BLOCKS_PER_SECTOR)) {
            num_block = I93_ONS_BLOCKS_PER_SECTOR -
                    (first_block % I93_ONS_BLOCKS_PER_SECTOR);
        }
    }

    return num_block;
}

static tNCI_STATUS multi_read_next_blocks(uint32_t first_block, tNCI_BUF* p_rsp)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint32_t num_block = RW_I93_READ_MULTI_BLOCK_SIZE / p_i93->block_size;

    // first_block is an offset related to the beginning of the T5T_Area for T5T
    // tags but physical memory for ISO15693 tags
    if (p_i93->i93_t5t_mode == RW_I93_GET_SYS_INFO_MEM_INFO) {
        if (num_block + first_block > p_i93->num_block)
            num_block = p_i93->num_block - first_block;
    } else {
        if (num_block + first_block > p_i93->num_block + p_i93->t5t_area_start_block)
            num_block = p_i93->num_block - first_block + p_i93->t5t_area_start_block;
    }

    if (p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ST) {
        /* LRIS64K, M24LR64-R, M24LR04E-R, M24LR16E-R, M24LR16D-W, M24LR64E-R
        ** require
        ** - The max number of blocks is 32 and they are all located in the
        **   same sector.
        ** - The sector is 32 blocks of 4 bytes.
        */
        if ((p_i93->product_version == RW_I93_ST_LRIS64K) ||
            (p_i93->product_version == RW_I93_ST_M24LR64_R) ||
            (p_i93->product_version == RW_I93_ST_M24LR04E_R) ||
            (p_i93->product_version == RW_I93_ST_M24LR16E_R) ||
            (p_i93->product_version == RW_I93_ST_M24LR16D_W) ||
            (p_i93->product_version == RW_I93_ST_M24LR64E_R)) {
            if (num_block > I93_ST_MAX_BLOCKS_PER_READ)
                num_block = I93_ST_MAX_BLOCKS_PER_READ;

            if ((first_block / I93_ST_BLOCKS_PER_SECTOR) !=
                ((first_block + num_block - 1) / I93_ST_BLOCKS_PER_SECTOR)) {
                num_block = I93_ST_BLOCKS_PER_SECTOR -
                        (first_block % I93_ST_BLOCKS_PER_SECTOR);
            }
        }
    }

    if (p_i93->uid[I93_UID_MFG_CODE_IND] == I93_UID_IC_MFG_CODE_ONS) {
        num_block = recalculate_num_block_ones(first_block, num_block);
    }

    if (num_block == 0) {
        /* only one remaining block to read */
        NFCLOG_I("only one remaining block to read");
        return rw_i93_send_cmd_read_single_block(first_block, false, p_rsp);
    }

    return rw_i93_send_cmd_read_multi_blocks(first_block, num_block, p_rsp);
}

tNCI_STATUS rw_i93_get_next_blocks(uint32_t offset, tNCI_BUF* p_rsp)
{
    tT5T_INFO *p_i93 = &s_t5t_info;
    uint32_t first_block;
    // uint32_t num_block;

    NFCLOG_D("enter");

    first_block = offset / p_i93->block_size;

    /* more blocks, more efficent but more error rate */

    if (p_i93->intl_flags & RW_I93_FLAG_READ_MULTI_BLOCK) {
        NFCLOG_I("read multi blocks");
        return multi_read_next_blocks(first_block, p_rsp);
    } else {
        return rw_i93_send_cmd_read_single_block(first_block, false, p_rsp);
    }
}

#endif // IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)
#endif //IS_FEATURE_ON(NCI_FEATURE_RW_T5T)